package cz.cuni.amis.pogamut.episodic.decisions;

import cz.cuni.amis.pogamut.episodic.episodes.EpisodeNode;
import cz.cuni.amis.pogamut.episodic.schemas.SchemaEpisodeNode;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * <code>Node</code> class is an abstract class representing nodes from
 * the decision tree in decision making process in pogamut end part of
 * the project. Node can be either <code>Action</code> (the OR node),
 * <code>Intention</code> (goal, the AND node) or <code>AtomicAction</code>.
 * <p>
 * It is possible to attach affordance slots to the <code>Node</code>s in
 * the decision tree, but no affordances should be attached to atomic actions.
 * All nodes that were performed at least once by an agent are associated
 * with equivalent nodes in the schema tree in agent's memory.
 * <p>
 * Every <code>EpisodeNode</code> in agent's memory of experienced episodes
 * is associated with its equivalent <code>Node</code> in decision trees.
 * The only way how to add a new memories to agent's memory is to provide
 * a specific trace of <code>Node</code>s starting with a top-level goal and
 * ending with atomic action.
 *
 * @author Michal Cermak
 * @see Action
 * @see Intention
 * @see AtomicAction
 * @see EpisodeNode
 */
public abstract class Node implements INode, Serializable {
    /**
     * Determines if a de-serialized file is compatible with this class.
     *
     * Maintainers must change this value if and only if the new version
     * of this class is not compatible with old versions. See Sun docs
     * for <a href=http://java.sun.com/products/jdk/1.1/docs/guide
     * /serialization/spec/version.doc.html> details. </a>
     *
     * Not necessary to include in first version of the class, but
     * included here as a reminder of its importance.
     */
    private static final long serialVersionUID = 1L;

    /**
     * Name of the node is used to identify and index the nodes agent's memory.
     * Therefore no two nodes in the same level can have identical names (e.g.
     * no two top-level goals, no two children of another node). Also node
     * cannot have a descendant (child, child's child...) with identical name.
     * There can be two nodes with same name though, if they are not children
     * of the same parent node and one is not an ancestor (parent,
     * grandparent...) of the other one.
     */
    private final String name;

    /**
     * Specified in XML file. Helps determine when to forget node.
     */
    private final int attractivity;

    /**
     * A reference to the parent node in the decision tree. Parents of
     * intentions (goals) and atomic action will be actions. Parents
     * of actions will be intention nodes. Only nodes without a parent are
     * the top-level goals - roots of decision tree.
     */
    public Node parent = null;

    /**
     * Unique ID of the node. Used as an ID of vertex representing
     * this node when visualizing decision tree.
     */
    private int id = 0;

    /**
     * Because ID of decision nodes and slots are not set automatically
     * this indicator determines whether it is OK to retreive the ID.
     * ID that was not set yet, cannot be retreived.
     */
    private boolean idSet = false;

    /**
     * Type of the <code>Node</code>. Node can be either action,
     * intention (goal) or atomic action.
     */
    private final NodeType type;

    /**
     * Actions and Intetions can have affordance slots attached to them. These
     * slots are kept in the <code>affordances</code> list. In order to
     * perform an action or satisfy a goal, all its slots have to be filled
     * first. Once the slot is filled, its equivalent in memory of experienced
     * episode will be linked with the object that satisfied the slot.
     */
    private Map<String, AffordanceSlot> affordances = new HashMap<String, AffordanceSlot>();

    /**
     * All nodes are associated with equivalent nodes in schemabag once
     * the equivalent node is created there. SchemaNodes can be used to
     * create average episodes or count number of experienced of an episode.
     * @see SchemaEpisodeNode
     */
    private SchemaEpisodeNode associatedSchemaNode = null;

    /** Returns a <code>String</code> object representing this
     * <code>EpisodeNode</code>'s info. It is used to provide tooltips
     * for the node when it is invoked from the visualizion structures.
     *
     * @return  A string representation of the value of this object,
     * consisting of its ID and name.
     */
    @Override
    public String toString() {
        return ((Integer)id).toString() + " " + name;
    }

    /**
     * This method sets the unique ID of the node. Once set it cannot be changed.
     * It is the ID of vertex representing this node when visualizing decision tree.
     *
     * @param newId The ID of this node.
     */
    public boolean setId(int newId) {
        if (idSet) return false;
        idSet = true;
        id = newId;
        return true;
    }

    /**
     * Getter method for the <code>id</code> variable.
     *
     * @return  Returns the unique ID of the slot that is used as an ID of vertex representing
     * this node when visualizing decision tree.
     * @throws  Throws exception when trying to retrive ID that was not set yet.
     */
    public int getId() {
        if (!idSet) throw new RuntimeException("Node id not set.");
        return id;
    }

    /**
     * Instantiate the class by providing its name, attractivity and type.
     * <p>
     * Name of the node is used to identify and index the nodes agent's memory.
     * Therefore no two nodes in the same level can have identical names (e.g.
     * no two top-level goals, no two children of another node). There can be
     * two nodes with same name though, if they are not children of the same
     * parent node.
     * <p>
     * Type of the <code>Node</code> can be either action, intention (goal)
     * or atomic action.
     *
     * @param   _name   Name of the node.
     * @param   _attractivity   Specified in XML file. Helps determine when to forget node.
     * @param   _type   Type of the node.
     */
    public Node(String _name, int _attractivity, NodeType _type) {
        name = _name;
        type = _type;
        if (_attractivity < 0) _attractivity = 0;
        if (_attractivity > 100) _attractivity = 100;
        attractivity = _attractivity;
    }

    /**
     * Getter method for the <code>type</code> variable.
     *
     * @return  Returns the type of the <code>Node</code>. Node can be
     * either action, intention (goal) or atomic action.
     */
    public NodeType getType() {
        return type;
    }

    /**
     * Getter method for the <code>name</code> variable.
     *
     * @return  Returns the name of the node.
     */
    public String getName() {
        return name;
    }

    /**
     * Actions and Intetions can have <code>AffordanceSlots</code> attached
     * to them. These slots are kept in the <code>affordances</code> list.
     * In order to perform an action or satisfy a goal, all its slots have
     * to be filled first.
     * <p>
     * This method attaches a new slot to this <code>Node</code>. There should
     * be no two slots of same type attached to one node.
     *
     * @param affordance    Already instantiated <code>AffordanceSlot</code>
     * that should be attached to this node.
     */
    public void addAffordance(AffordanceSlot affordance) {
        affordances.put(affordance.getType(), affordance);
//        affordances.add(affordance);
    }

    /**
     * This method sets the <code>associatedSchemaNode</code> variable.
     * All nodes are associated with equivalent nodes in schemabag once
     * the equivalent node is created there. SchemaNodes can be used to
     * create average episodes or count number of experienced of an episode.
     * <p>
     * It should only be called from the newly created schema node when
     * making the <strong>mutual</strong> association. New schema node
     * association to this node has to be performed first, otherwise
     * the association is not made.
     *
     * @param   node    Newly created <code>SchemaEpisodeNode</code> that this
     * node should be associated with.
     * @return  Returns true if the association is mutual and associating
     * was successful, returns false otherwise.
     * @see SchemaEpisodeNode
     */
    public boolean setAssociatedSchemaNode(SchemaEpisodeNode node) {
  //      if (node.getAssociatedNode().id == id) {
            associatedSchemaNode = node;
            return true;
  //      }
  //      return false;
    }

    /**
     * Getter method for the <code>associatedSchemaNode</code> variable.
     *
     * @return  Returns a reference to equivalent node in schemabag once
     * the equivalent node is created there. SchemaNodes can be used to
     * create average episodes or count number of experienced of an episode.
     * Returns null of the associated node does not exist yet.
     * @see SchemaEpisodeNode
     */
    @Override
    public SchemaEpisodeNode getAssociatedNode() {
        return associatedSchemaNode;
    }

    /**
     * Getter method for the <code>affordances</code> variable.
     *
     * @return  Returns a list of affordance slots attached to this node.
     * In order to perform an action or satisfy a goal, all its slots
     * have to be filled first. Once the slot is filled, its equivalent
     * in memory of experienced episode will be linked with the object
     * that satisfied the slot.
     */
    @Override
    public Collection<AffordanceSlot> getAffordances() {
        return affordances.values();
    }

    public AffordanceSlot getAffordance(String type) {
        return affordances.get(type);
    }

    /**
     * A getter method for <code>attractivity</code> variable.
     * <p>
     * Specified in XML file. Helps determine when to forget node.
     *
     * @return  Returns the attractivity of this node. Value between 0-100.
     * 0 means unattractive, 100 means very attractive.
     */
    public int getAttractivity() {
        return attractivity;
    }
}
